#ifndef __tim_H
#define __tim_H
#ifdef __cplusplus
 extern "C" {
#endif

#include "bsp/bsp.h"
#include "bsp/cpu_ticks.h"
#include "os/types.h"
#include "os/os.h"

extern TIM_HandleTypeDef htim1;
extern TIM_HandleTypeDef htim_aux;
#ifdef MAG_ENC_TIMER
extern TIM_HandleTypeDef htim_enc;
#endif
#ifdef HALL_TIMER
extern TIM_HandleTypeDef htim_hall;
#endif
extern TIM_HandleTypeDef htim_sch;

extern bool brake_resistor_armed;
#define TMR1_PWM_CHAN_012MASK (TIM_CCER_CC1E|TIM_CCER_CC1NE|TIM_CCER_CC2E|TIM_CCER_CC2NE|TIM_CCER_CC3E|TIM_CCER_CC3NE)
#define pwm_enable_channel() (PWM_TIMER->CCER |= TMR1_PWM_CHAN_012MASK)
#define pwm_disable_channel() (PWM_TIMER->CCER &= ~TMR1_PWM_CHAN_012MASK)

#define PWM_ENABLE_ADC_TRIGGER() (PWM_TIMER->CCER |= TIM_CCER_CC4E)

#define __HAL_TIM_SET_REPEAT(__HANDLE__, __RCR__) (__HANDLE__)->Instance->RCR = (__RCR__)

#define pwm_update_duty(a, b, c) \
do { \
	TIM_TypeDef *TIMx = PWM_TIMER;\
	TIMx->CCR3 = a; \
	TIMx->CCR2 = b; \
	TIMx->CCR1 = c; \
}while(0)

#define pwm_count() __HAL_TIM_GET_COUNTER(&htim1);

#define pwm_update_adc_trigger(s) {PWM_TIMER->CCR4 = s;}

__STATIC_INLINE void LL_TIM_GenerateEvent_UPDATE(TIM_TypeDef *TIMx);

static __INLINE void pwm_enable_channel_pump(void) {
	u32 mask = cpu_enter_critical();
	pwm_update_duty(0 ,0, 0);
	LL_TIM_GenerateEvent_UPDATE(htim1.Instance);
	__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
	while(!__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE));
	__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);

	pwm_enable_channel();
	cpu_udelay(100); //give the time to charger pump
	cpu_exit_critical(mask);
}


#if CONFIG_USE_MAG_ENCODER
static __INLINE u8 _enc_direction(void) {
	if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim_enc)) {
		return ENCODER_TIMER_DIR_DOWN;
	}else {
		return ENCODER_TIMER_DIR_UP;
	}
}

static __INLINE u32 _enc_count(void) {
	return __HAL_TIM_GET_COUNTER(&htim_enc);
}

static __INLINE void _enc_set_count(u16 count) {
	__HAL_TIM_SET_COUNTER(&htim_enc, count);
}

static __INLINE bool _enc_timer_clear_overflow(void) {
	if (__HAL_TIM_GET_FLAG(&htim_enc, TIM_FLAG_UPDATE)) {
		__HAL_TIM_CLEAR_FLAG(&htim_enc, TIM_FLAG_UPDATE);
		return true;
	}
	return false;
}
#endif
static __INLINE void pwm_enable_brake(bool brake){
	__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_BREAK);
#ifdef PWM_TIMER_BRK_IN_PIO
    if (brake) {
        __HAL_TIM_ENABLE_IT(&htim1, TIM_IT_BREAK);
    }else {
		__HAL_TIM_DISABLE_IT(&htim1, TIM_IT_BREAK);
	}
#endif
}

static __INLINE bool timer_wait_clear_update(void) {
	if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_UPDATE)) {
		__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_UPDATE);
		return true;
	}
	return false;
}

static __INLINE bool timer_wait_clear_brk(void) {
	if (__HAL_TIM_GET_FLAG(&htim1, TIM_FLAG_BREAK)) {
		__HAL_TIM_CLEAR_FLAG(&htim1, TIM_FLAG_BREAK);
		return true;
	}
	return false;
}

static __INLINE bool timer_sch_update(void) {
	if (__HAL_TIM_GET_FLAG(&htim_sch, TIM_FLAG_UPDATE)){
		__HAL_TIM_CLEAR_FLAG(&htim_sch, TIM_FLAG_UPDATE);
		return true;
	}
	return false;
}

#ifdef MAG_ENC_Z_IRQ_FLAGS
static __INLINE bool timer_enc_z_wait_clear(void) {
	if (__HAL_TIM_GET_FLAG(&htim_enc, MAG_ENC_Z_IRQ_FLAGS)) {
		__HAL_TIM_CLEAR_FLAG(&htim_enc, MAG_ENC_Z_IRQ_FLAGS);
		return true;
	}
	return false;
}
#endif


#ifdef CONFIG_CPU_TICK_TIMER
void cpu_ticks_timer_init(void);
static __INLINE u32 cpu_tick_count(void) {
	return CONFIG_CPU_TICK_TIMER->CNT;
}
#endif

static __INLINE bool apply_brake_resistor_duty(float brake_duty) {
#ifdef AUX_TIMER
    int high_on = (int)(AUX_TIM_PERIOD_CLOCKS * (1.0f - brake_duty));
    int low_off = high_on - AUX_TIM_DEADTIME_CLOCKS;
    if (low_off < 0) low_off = 0;

    if (high_on - low_off < AUX_TIM_DEADTIME_CLOCKS) {
        return false;
    }

    __disable_irq();
    if (brake_resistor_armed) {
        // Safe update of low and high side timings
        // To avoid race condition, first reset timings to safe state
        // ch3 is low side, ch4 is high side
        htim_aux.Instance->CCR3 = 0;
        htim_aux.Instance->CCR4 = AUX_TIM_PERIOD_CLOCKS + 1;
        htim_aux.Instance->CCR3 = low_off;
        htim_aux.Instance->CCR4 = high_on;
    }
	__enable_irq();
#endif
	return true;
}

#ifndef LL_TIM_GenerateEvent_UPDATE
__STATIC_INLINE void LL_TIM_GenerateEvent_UPDATE(TIM_TypeDef *TIMx)
{
  SET_BIT(TIMx->EGR, TIM_EGR_UG);
}
#endif

void mc_sched_timer_init(int ms);
void pwm_timer_init(u32 freq);
void aux_timer_init(void);
void encoder_abi_timer_init(u32 max_res);
void pwm_start_output(void);
void pwm_stop_output(void);
void pwm_enable_update_irq(bool en);

#ifdef __cplusplus
}
#endif
#endif /*__ tim_H */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
